package cn.calm.xhtml.contentassist.autocomplete;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;

import javax.xml.namespace.QName;


import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.jface.text.BadLocationException;
import org.eclipse.jface.text.IDocument;
import org.eclipse.jface.text.ITextViewer;
import org.eclipse.jface.text.contentassist.ICompletionProposal;
import org.eclipse.wst.sse.core.internal.provisional.text.IStructuredDocument;
import org.eclipse.wst.sse.core.internal.provisional.text.IStructuredDocumentRegion;
import org.eclipse.wst.sse.core.internal.provisional.text.ITextRegion;
import org.eclipse.wst.sse.core.internal.provisional.text.ITextRegionList;
import org.eclipse.wst.sse.ui.contentassist.CompletionProposalInvocationContext;
import org.eclipse.wst.sse.ui.contentassist.ICompletionProposalComputer;
import org.eclipse.wst.sse.ui.internal.contentassist.ContentAssistUtils;
import org.eclipse.wst.xml.core.internal.provisional.document.IDOMNode;
import org.eclipse.wst.xml.core.internal.regions.DOMRegionContext;
import org.w3c.dom.Attr;
import org.w3c.dom.Element;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;

import cn.calm.xhtml.XhtmlActivator;

@SuppressWarnings("restriction")
public class Autocomplete implements ICompletionProposalComputer{

	@Override
	public List<?> computeCompletionProposals(
			CompletionProposalInvocationContext context, IProgressMonitor arg1) {
		ArrayList<ICompletionProposal> proposals = new ArrayList<ICompletionProposal>();
		try {
			ITextViewer viewer = context.getViewer();
			IStructuredDocument document = (IStructuredDocument)context.getDocument();
			int cursorposition = context.getInvocationOffset();
	
			IDOMNode node = (IDOMNode)ContentAssistUtils.getNodeAt(viewer, cursorposition);
			IStructuredDocumentRegion documentregion = ContentAssistUtils.getStructuredDocumentRegion(
					viewer, cursorposition);
			ITextRegion textregion = documentregion.getRegionAtCharacterOffset(cursorposition);
			// Figure out which type of suggestions to make
		
			if (makeElementProcessorSuggestions(node, textregion, documentregion, document, cursorposition)) {
				proposals.addAll(computeElementProcessorSuggestions(node, document, cursorposition));
			}else if (makeAttrProcessorSuggestions(node, textregion, documentregion, document, cursorposition)) {
				proposals.addAll(computeAttrProcessorSuggestions(node, document, cursorposition));
			}
		} catch (BadLocationException e) {
			e.printStackTrace();
		}
		return proposals;
	}

	@SuppressWarnings("unchecked")
	private Collection<? extends ICompletionProposal> computeAttrProcessorSuggestions(
			IDOMNode node, IStructuredDocument document, int cursorposition) throws BadLocationException {

		String pattern = findProcessorNamePattern(document, cursorposition);
		List<AttrProcessorCompletionProposal> processors = TagCache.getAttrProcessors(
				XhtmlActivator.findCurrentJavaProject(), node, pattern);
		if (!processors.isEmpty()) {
//			ArrayList<ElementProcessorCompletionProposal> proposals =
//					new ArrayList<ElementProcessorCompletionProposal>();
//			for (ElementProcessorCompletionProposal processor: processors) {
//				proposals.add(new ElementProcessorCompletionProposal(processor,
//						pattern.length(), cursorposition));
//			}
			return processors;
		}

		return Collections.EMPTY_LIST;
	}

	private boolean makeAttrProcessorSuggestions(IDOMNode node,
			ITextRegion textregion, IStructuredDocumentRegion documentregion,
			IStructuredDocument document, int cursorposition) throws BadLocationException {
		if (node.getNodeType() == IDOMNode.ELEMENT_NODE) {
			if (Character.isWhitespace(document.getChar(cursorposition - 1))) {
				return true;
			}
			if (textregion.getType() == DOMRegionContext.XML_TAG_ATTRIBUTE_NAME) {
				return true;
			}
			ITextRegionList textregionlist = documentregion.getRegions();
			ITextRegion previousregion = textregionlist.get(textregionlist.indexOf(textregion) - 1);
			if (previousregion.getType() == DOMRegionContext.XML_TAG_ATTRIBUTE_NAME) {
				return true;
			}
		}
		return false;
	}

	/**
	 * Collect element processor suggestions.
	 * 
	 * @param node
	 * @param document
	 * @param cursorposition
	 * @return List of element processor suggestions.
	 * @throws BadLocationException
	 */
	@SuppressWarnings("unchecked")
	private static List<ElementProcessorCompletionProposal> computeElementProcessorSuggestions(
		IDOMNode node, IStructuredDocument document, int cursorposition) throws BadLocationException {
		
		String pattern = findProcessorNamePattern(document, cursorposition);
		List<ElementProcessorCompletionProposal> processors = TagCache.getElementProcessors(
				XhtmlActivator.findCurrentJavaProject(), findNodeNamespaces(node), pattern);
		if (!processors.isEmpty()) {
//			ArrayList<ElementProcessorCompletionProposal> proposals =
//					new ArrayList<ElementProcessorCompletionProposal>();
//			for (ElementProcessorCompletionProposal processor: processors) {
//				proposals.add(new ElementProcessorCompletionProposal(processor,
//						pattern.length(), cursorposition));
//			}
			return processors;
		}

		return Collections.EMPTY_LIST;
	}
	/**
	 * Return a list of the namespaces valid at the given node.
	 * 
	 * @param node
	 * @return List of namespaces known to this node.
	 */
	protected static ArrayList<QName> findNodeNamespaces(Node node) {

		ArrayList<QName> namespaces = new ArrayList<QName>();

		if (node instanceof Element) {
			NamedNodeMap attributes = node.getAttributes();
			for (int i = 0; i < attributes.getLength(); i++) {
				String name = ((Attr)attributes.item(i)).getName();
				if (name.startsWith("xmlns:")) {
					namespaces.add(new QName(((Element)node).getAttribute(name), "", name.substring(6)));
				}
			}
		}
		Node parent = node.getParentNode();
		if (parent != null) {
			namespaces.addAll(findNodeNamespaces(parent));
		}

		return namespaces;
	}
	/**
	 * Return the processor name pattern before the cursor position.
	 * 
	 * @param document
	 * @param cursorposition
	 * @return The text entered up to the document offset, if the text could
	 * 		   constitute a processor name.
	 * @throws BadLocationException
	 */
	private static String findProcessorNamePattern(IDocument document, int cursorposition)
		throws BadLocationException {

		int position = cursorposition;
		int length = 0;
		while (--position > 0 && isProcessorChar(document.getChar(position))) {
			length++;
		}
		return document.get(position + 1, length).trim();
	}
	
	/**
	 * Returns whether or not the given character is a valid processor name
	 * character.
	 * 
	 * @param c
	 * @return <tt>true</tt> if <tt>char</tt> is an alphanumeric character, or
	 * 		   one of the following symbols: <tt>: -</tt>
	 */
	protected static boolean isProcessorChar(char c) {

		return Character.isLetterOrDigit(c) || c == ':' || c == '-'|| c == ' ';
	}
	/**
	 * Check if, given everything, element processor suggestions should be made.
	 * 
	 * @param node
	 * @param textregion
	 * @param documentregion
	 * @param document
	 * @param cursorposition
	 * @return <tt>true</tt> if element processor suggestions should be made.
	 * @throws BadLocationException
	 */
	private static boolean makeElementProcessorSuggestions(IDOMNode node, ITextRegion textregion,
		IStructuredDocumentRegion documentregion, IStructuredDocument document, int cursorposition)
		throws BadLocationException {

		switch (node.getNodeType()) {

		// If we're in a text node, then the first non-whitespace character before
		// the cursor in the document should be an opening bracket
		case IDOMNode.TEXT_NODE:
			int position = cursorposition - 1;
			while (position >= 0 && Character.isWhitespace(document.getChar(position))) {
				position--;
			}
			if (document.getChar(position) == '<'||document.getChar(position) == '>') {
				return true;
			}
			break;

		// If we're in an element node, then the previous text region should be an
		// opening XML tag
		case IDOMNode.ELEMENT_NODE:
			ITextRegionList textregionlist = documentregion.getRegions();
			int currentregionindex = textregionlist.indexOf(textregion);
			try {
				ITextRegion previousregion = textregionlist.get(currentregionindex - 1);
				if ((previousregion.getType() == DOMRegionContext.XML_TAG_OPEN) &&
					!Character.isWhitespace(document.getChar(cursorposition - 1))) {
					return true;
				}
			}
			catch (ArrayIndexOutOfBoundsException ex) {
			}
			break;
		}

		return false;
	}
	
	@Override
	public List<?> computeContextInformation(
			CompletionProposalInvocationContext arg0, IProgressMonitor arg1) {
		return null;
	}

	@Override
	public String getErrorMessage() {
		return null;
	}

	@Override
	public void sessionEnded() {
		
	}

	@Override
	public void sessionStarted() {
		
	}


}
